package com.hup.sidebar.view.touch;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Vibrator;
import android.text.Layout.Alignment;
import android.text.StaticLayout;
import android.text.StaticLayout.Builder;
import android.text.TextPaint;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;

import com.hup.sidebar.R;
import com.hup.sidebar.SideBarApplication;
import com.hup.sidebar.androidService.AppService;
import com.hup.sidebar.constant.PieFunctionConstant;
import com.hup.sidebar.model.AppConfig;
import com.hup.sidebar.model.MultiPieFunction;
import com.hup.sidebar.model.PieFunction;
import com.hup.sidebar.view.NoteActivity;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;

import lombok.Getter;
import lombok.ToString;

/**
 * 扇形view;
 *
 * @author hugan
 * @apiNote <pre>
 * 只支持 {@link ActivationView}中使用<br>
 * 虽然代码中使用 {@link Gravity#START},但实际是不支持right-to-left的!!!
 * @date 2022/6/3
 */
@SuppressLint("ViewConstructor")
public class PieView extends View {
    private static final String TAG = PieView.class.getSimpleName();
    /**
     * 选中[编辑便签]时的index
     */
    private static final int SELECTED_NOTE_INDEX = -2;
    /**
     * 选中[临时关闭]时的index
     */
    private static final int SELECTED_TEMP_INDEX_INDEX = -3;
    private static final int NOTE_MARGIN_H = 50;//便签边框的水平margin
    private static final int NOTE_PADDING = 20;//便签边框的padding

    /**
     * {@link Gravity#START}, {@link Gravity#END},
     */
    @Getter
    private final int gravity;
    private final AppConfig appConfig;
    private final LongPressAction longPressAction = new LongPressAction();

    private boolean isActive = false;

    //绘制属性
    private final Paint paintPie;
    private final Paint paintIndicator;
    /**
     * 用于绘制图标时,实现旋转和位移
     */
    private final Matrix iconMatrix = new Matrix();
    /**
     * 每块扇区的度数
     */
    private float pieDegree;
    /**
     * 扇区的绘制参数
     * 左右两边:都是从上向下
     */
    private final ArrayList<DrawParam> drawParams = new ArrayList<>();

    //状态属性
    /**
     * 当前view的根window尺寸,排除状态栏等
     */
    private final Rect rectWindow = new Rect();
    /**
     * 扇形圆心:基于[激活器按下事件]计算后的坐标
     */
    private final Point centerLoc = new Point();
    /**
     * MotionEvent坐标,相对于当前view
     */
    private final Point touchLoc = new Point();
    /**
     * 扇形的绘制区域,会超出屏幕
     */
    private final RectF rectPie = new RectF();
    /**
     * 当前选中的分区,无选中为-1,
     * 便签:SELECTED_NOTE_INDEX
     */
    private int selectedPieIndex = -1;
    /**
     * 当前按键状态:长按/短按
     */
    private boolean longPressed;

    //便签
    private final int noteBarHeight;
    private final TextPaint paintCommon;
    private final TextPaint paintNote;
    private StaticLayout noteLayout;
    /**
     * 文本宽度
     */
    private float noteWidth;

    public PieView(Context context, int gravity, AppConfig appConfig) {
        super(context);
        setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
        this.gravity = gravity;
        this.appConfig = appConfig;

        paintPie = new Paint();
        paintPie.setAntiAlias(true);
        paintPie.setStyle(Style.STROKE);
        paintPie.setStrokeWidth(appConfig.pieWidth);
        paintIndicator = new Paint();
        paintIndicator.setColor(Color.RED);
        //便签
        TypedValue typedValue = new TypedValue();
        if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize, typedValue, true)) {
            noteBarHeight = TypedValue.complexToDimensionPixelSize(typedValue.data, getResources().getDisplayMetrics());
        } else {
            noteBarHeight = 150;
        }
        paintCommon = new TextPaint();
        paintCommon.setAntiAlias(true);
        paintCommon.setStyle(Style.FILL);
        paintCommon.setFakeBoldText(true);
        float noteFontSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, getResources().getDisplayMetrics());
        paintCommon.setTextSize(noteFontSize);
        paintNote = new TextPaint(paintCommon);

        onPieConfigUpdated();
    }

    /**
     * @param e 由 {@link ActivationView} 传递过来的事件
     */
    public void onMyTouchEvent(MyMotionEvent e) {
        /*
        获取相对于当前view(铺满屏幕)的touchLoc
        因为 ActivationView在[e创建,传递到此处时] view大小已改变,所以要根据raw和rect重新计算!
         */
        int x = e.getRawX() - rectWindow.left;
        int y = e.getRawY() - rectWindow.top;
        touchLoc.set(x, y);

        int action = e.getActionMasked();
        if (action == MotionEvent.ACTION_DOWN) {
            Log.d(TAG, "onTouchEvent: " + touchLoc + ", " + e);
            isActive = true;
            selectedPieIndex = -1;
            longPressed = false;
            getWindowVisibleDisplayFrame(rectWindow);
            updateRectParams(y);
            invalidate();
        } else if (action == MotionEvent.ACTION_MOVE) {
            boolean draw = false;
            int selectedPieCur = getSelectedPieIndex();
            if (selectedPieCur != selectedPieIndex) {
                Log.d(TAG, "onTouchEvent: selectPie.new,old=" + selectedPieCur + "," + selectedPieIndex);
                if (selectedPieCur == -1) {
                    longPressed = false;
                    removeCallbacks(longPressAction);
                } else if (selectedPieIndex == -1) {
                    removeCallbacks(longPressAction);
                    postDelayed(longPressAction, appConfig.pieLongPressTime);
                }
                selectedPieIndex = selectedPieCur;
                draw = true;
            }

            if (draw || appConfig.pieShowIndicator) {
                invalidate();
            }
        } else if (action == MotionEvent.ACTION_UP) {
            //removeCallbacks(longPressAction);//此处调用会无效! 因为ActivationView已经把this移除了
            if (selectedPieIndex == -1) return;
            if (selectedPieIndex == SELECTED_NOTE_INDEX) {
                Log.d(TAG, "编辑便签: ");
                /*
                坑
                android:launchMode="singleInstance"
                    能保证单例
                    但 main>NoteActivity>返回桌面>再打开NoteActivity,就会卡顿好几秒...
                 */
                Intent intent = new Intent(getContext(), NoteActivity.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                getContext().startActivity(intent);
            } else if (selectedPieIndex == SELECTED_TEMP_INDEX_INDEX) {
                SideBarApplication.executeIfExist(AppService.class, AppService::doTempHide);
            } else if (selectedPieIndex > -1 && !appConfig.functions.isEmpty()) {
                MultiPieFunction multi = appConfig.functions.get(selectedPieIndex);
                PieFunction function = (longPressed && multi.longPress != null) ? multi.longPress : multi.normal;
                Log.d(TAG, "onTouchEvent: 触发功能=" + function);
                if (function != null) {
                    PieFunctionConstant.fireAction(getContext(), function, appConfig);
                }
            }
            isActive = false;
        }
    }

    /**
     * @return 当前选中的分区index
     */
    private int getSelectedPieIndex() {
        if (touchLoc.y < noteBarHeight) {
            if (appConfig.noteShow && appConfig.tempHideShow) {
                return touchLoc.x < rectWindow.centerX() ? SELECTED_NOTE_INDEX : SELECTED_TEMP_INDEX_INDEX;
            } else if (appConfig.noteShow) {
                return SELECTED_NOTE_INDEX;
            } else if (appConfig.tempHideShow) {
                return SELECTED_TEMP_INDEX_INDEX;
            }
        }

        //不区分[左右下]
        int x = touchLoc.x - centerLoc.x;
        int y = touchLoc.y - centerLoc.y;

        double touchRadius = Math.sqrt(x * x + y * y);//半径
        if (touchRadius < appConfig.pieRadius - appConfig.pieWidth) return -1;

        /*
        https://www.freesion.com/article/2828124377/
        计算时,不区分x,y轴/正反方向, 不区分[左右下]
            统一在下面处理偏移量,根据实际log,进行计算
            因为:drawArc的角度与数学坐标系都不一致,左右两边扇形是镜像布局/角度递增方向不一样
         */
        double radians = Math.atan2(y, x);//弧度
        double degrees = Math.toDegrees(radians);//角度
        //Log.i(TAG, "getSelectedPie: " + x + " ," + y + " ," + touchRadius + " ," + radians + " ," + degrees);
        if (gravity == Gravity.START) {
            //startAngles=[-90.0, -44.75, 0.5, 45.75],顺时针,起始于顶部
            for (int i = drawParams.size() - 1; i >= 0; i--) {
                float startAngle = drawParams.get(i).startAngle;
                if (degrees > startAngle) return i;
            }
        } else if (gravity == Gravity.END) {
            /*
            startAngles=[90.0, 135.25, 180.5, 225.75],顺时针,起始于底部
            degrees右侧情况
                圆点上方:从下往上,[-180,-90]度
                圆点下方:从下往上,[90,180]度
             */
            if (degrees < 0) degrees += 360;
            for (int i = 0; i < drawParams.size(); i++) {
                float startAngle = drawParams.get(i).startAngle;
                if (degrees > startAngle) return i;
            }
        }
        return -1;
    }

    /**
     * 按下事件后, 更新扇形绘制区域
     */
    private void updateRectParams(int y) {
        //此处就是Gravity.LEFT, 不支持right-to-left!!
        if (gravity == Gravity.START) {
            centerLoc.x = 0;
        } else if (gravity == Gravity.END) {
            centerLoc.x = rectWindow.right - rectWindow.left;
        }
        centerLoc.y = y;

        /*
        扇形绘制区域
        radius,绘制半径,注意:paint.STROKE是居中描边的,所以要减 pieWidth / 2f
         */
        float radius = appConfig.pieRadius - appConfig.pieWidth / 2f;
        rectPie.top = centerLoc.y - radius;
        rectPie.bottom = centerLoc.y + radius;
        if (gravity == Gravity.START) {
            rectPie.left = -radius;
            rectPie.right = radius;
        } else if (gravity == Gravity.END) {
            rectPie.left = centerLoc.x - radius;
            rectPie.right = centerLoc.x + radius;
        }
        //note
        if (appConfig.noteShow && (!appConfig.noteData.isEmpty() || appConfig.noteDateShow)) {
            String text;
            if (appConfig.noteDateShow) {
                //添加日期
                String date = appConfig.noteDateFormatter.format(new Date());
                text = (date + "\n" + appConfig.noteData).trim();//trim处理只有date
            } else {
                text = appConfig.noteData;
            }

            //构建 文本绘制图层
            //文本宽度: 窗口宽度-单侧扇形宽度-边距
            int maxTextWidth = rectWindow.width() - appConfig.pieRadius - NOTE_MARGIN_H * 2 - NOTE_PADDING * 2;
            if (VERSION.SDK_INT >= VERSION_CODES.M) {
                noteLayout = Builder.obtain(null, 0, 0, paintNote, maxTextWidth)
                        .setText(text)
                        .build();
            } else {
                noteLayout = new StaticLayout(text, paintNote,
                        maxTextWidth, Alignment.ALIGN_NORMAL,
                        1, 0, true);
            }
            //获取最大行宽
            noteWidth = 0;
            for (int i = noteLayout.getLineCount() - 1; i >= 0; i--) {
                //实际绘制的行,比原文本多了'自动换行'的产生的行
                float width = noteLayout.getLineWidth(i);
                if (width > noteWidth) noteWidth = width;
            }
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //Log.i(TAG, "onDraw: " + drawParams);
        //Log.i(TAG, "onDraw: " + isActive);
        if (!isActive) return;//很重要, 不然会异常或错画

        //canvas.drawColor(Color.parseColor("#900000ff"));//test,背景色,观察窗口范围

        //绘制扇区
        int size = drawParams.size();
        boolean noneFunction = appConfig.functions.isEmpty();
        for (int i = 0; i < size; i++) {
            //绘制扇形
            DrawParam drawParam = drawParams.get(i);
            float startAngle = drawParam.startAngle;
            paintPie.setColor(i == selectedPieIndex ? appConfig.pieColorSelected : appConfig.pieColor);
            canvas.drawArc(rectPie, startAngle, pieDegree, false, paintPie);
            //Log.i(TAG, "onDraw: rectPie=" + rectPie + "," + startAngle + "," + pieDegree);

            if (noneFunction) break;
            //绘制图标
            MultiPieFunction multi = appConfig.functions.get(i);
            PieFunction function = (longPressed && multi.longPress != null) ? multi.longPress : multi.normal;
            if (function != null && function.cachedIcon != null) {
                /*
                过程: 移动图标到扇区中心,旋转图标
                https://blog.csdn.net/weixin_30760895/article/details/95092394
                    matrix.setXX: 清空之前的操作
                    matrix.postXX: 追加操作
                    matrix.preXX: 类似 list.insert(0, data)
                 */
                Bitmap bitmap = function.cachedIcon;
                int centerX = centerLoc.x + drawParam.iconCenterX - bitmap.getWidth() / 2;
                int centerY = centerLoc.y + drawParam.iconCenterY - bitmap.getHeight() / 2;
                iconMatrix.setTranslate(centerX, centerY);

                int rotateX = centerLoc.x + drawParam.iconCenterX;
                int rotateY = centerLoc.y + drawParam.iconCenterY;
                iconMatrix.postRotate(drawParam.iconRotate, rotateX, rotateY);
                canvas.drawBitmap(bitmap, iconMatrix, null);
            }
        }

        //顶部按钮[编辑便签,临时隐藏]
        int buttonWidth = rectWindow.width();
        if (appConfig.noteShow && appConfig.tempHideShow) {
            buttonWidth /= 2;
        }

        //便签
        if (appConfig.noteShow) {
            //顶部按钮
            paintCommon.setColor(selectedPieIndex == SELECTED_NOTE_INDEX ? appConfig.pieColorSelected : appConfig.pieColor);
            canvas.drawRect(0, 0, buttonWidth, noteBarHeight, paintCommon);
            paintCommon.setColor(appConfig.pieColorFont);
            canvas.drawText(getContext().getString(R.string.note_edit), 50, noteBarHeight - 50, paintCommon);
            //便签内容
            if (!appConfig.noteData.isEmpty() || appConfig.noteDateShow) {
                canvas.save();
                int left;
                if (gravity == Gravity.START) {
                    left = NOTE_MARGIN_H + appConfig.pieRadius;
                } else {
                    left = NOTE_MARGIN_H;
                }
                int top = noteBarHeight + 100;
                //背景框
                canvas.translate(left, top);
                paintNote.setColor(appConfig.pieColor);
                canvas.drawRoundRect(0, 0,
                        noteWidth + NOTE_PADDING * 2,
                        noteLayout.getHeight() + NOTE_PADDING * 2,
                        NOTE_PADDING, NOTE_PADDING, paintNote);
                //文本
                canvas.translate(NOTE_PADDING, NOTE_PADDING);
                paintNote.setColor(appConfig.pieColorFont);
                noteLayout.draw(canvas);
                //
                canvas.restore();
            }
        }

        //临时关闭
        if (appConfig.tempHideShow) {
            //顶部按钮
            paintCommon.setColor(selectedPieIndex == SELECTED_TEMP_INDEX_INDEX ? appConfig.pieColorSelected : appConfig.pieColor);
            int left = rectWindow.width() - buttonWidth;
            canvas.drawRect(left, 0, rectWindow.right, noteBarHeight, paintCommon);
            paintCommon.setColor(appConfig.pieColorFont);
            canvas.drawText(getContext().getString(R.string.main_temp_hide), left + 50, noteBarHeight - 50, paintCommon);
        }

        //显示指示器,用于测试观察
        if (appConfig.pieShowIndicator) {
            //扇形半径线
            if (gravity == Gravity.START) {
                canvas.drawLine(centerLoc.x, centerLoc.y, appConfig.pieRadius, centerLoc.y, paintIndicator);//test,观察绘制尺寸
            } else {
                canvas.drawLine(centerLoc.x, centerLoc.y, rectWindow.width() - appConfig.pieRadius, centerLoc.y, paintIndicator);//test,观察绘制尺寸
            }
            //跟随触点的线
            canvas.drawLine(centerLoc.x, centerLoc.y, touchLoc.x, touchLoc.y, paintIndicator);
        }
    }

    public void onRemoveFromScreen() {
        removeCallbacks(longPressAction);
    }

    /**
     * 根据appConfig,更新绘制参数
     */
    public void onPieConfigUpdated() {
        Log.i(TAG, "onPieConfigUpdated: ");
        paintPie.setStrokeWidth(appConfig.pieWidth);

        //扇区参数
        drawParams.clear();
        for (MultiPieFunction ignored : appConfig.functions) drawParams.add(new DrawParam());
        if (drawParams.isEmpty()) drawParams.add(new DrawParam());

        //绘制角度,按安卓的坐标系计算
        int count = drawParams.size();//扇区数量
        pieDegree = (180f - (count - 1) * appConfig.pieGap) / count;//扇区的度数
        if (pieDegree < 2) pieDegree = 2;//防止count和gap过大
        float halfDegree = pieDegree / 2;//扇区一半的度数
        int circleRadius = (int) (appConfig.pieRadius - appConfig.pieWidth / 2f);//图标所在圆心的半径
        //先按左扇形,计算扇区起始角度等,顺时针
        if (count == 1) {
            drawParams.get(0).startAngle = -90F;
        } else {
            for (int i = 0; i < count - 1; i++) {
                float startAngle = -90 + i * (pieDegree + appConfig.pieGap);
                drawParams.get(i).setValues(startAngle, circleRadius, halfDegree);
            }
            //最后一块(90 - pieDegree),保证结束点在180度
            drawParams.get(count - 1).setValues(90 - pieDegree, circleRadius, halfDegree);
        }
        //非左侧时,统一旋转
        if (gravity == Gravity.END) {
            for (int i = 0; i < count; i++) {
                DrawParam drawParam = drawParams.get(i);
                drawParams.get(i).setValues(drawParam.startAngle + 180, circleRadius, halfDegree);
            }
            Collections.reverse(drawParams);//倒序
        }
        //Log.i(TAG, "updateDrawParam: drawParams={}" + drawParams);

        float noteFontSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, appConfig.noteFontSize, getResources().getDisplayMetrics());
        paintNote.setTextSize(noteFontSize);
    }

    private class LongPressAction implements Runnable {

        private final Vibrator vibrator;

        public LongPressAction() {
            vibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
        }

        @Override
        public void run() {
            //Log.i(TAG, "run: LongPressAction " + appConfig.getConfigString());
            if (appConfig.pieVibrate) {
                if (appConfig.vibrateTime != 0) {
                    vibrator.vibrate(appConfig.vibrateTime);
                } else {
                    performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
                }
            }
            longPressed = true;
            invalidate();
        }
    }

    @ToString
    private static class DrawParam {
        /**
         * 扇区起始角度
         */
        float startAngle;
        /**
         * 扇区图标的中心点,相对于centerLoc
         */
        int iconCenterX;
        int iconCenterY;
        /**
         * 图标旋转度数
         */
        float iconRotate;

        /**
         * 计算和赋值
         *
         * @param circleRadius 图标所在圆心的半径
         */
        public void setValues(float startAngle, int circleRadius, float halfDegree) {
            this.startAngle = startAngle;
            /*
            已知[半径,角度],求圆上的点坐标
                https://blog.csdn.net/qq_33490514/article/details/111598900
             */
            float pieCenterDegree = startAngle + halfDegree;//扇区中心的角度
            iconCenterX = (int) (circleRadius * Math.cos(Math.toRadians(pieCenterDegree)));
            iconCenterY = (int) (circleRadius * Math.sin(Math.toRadians(pieCenterDegree)));
            /*
            图标旋转角度
                数学坐标系时,即求:圆上切线与x轴的夹角,即=90度-"垂直于切线的半径的角度"
                安卓坐标系时:试出来的...
             */
            iconRotate = 90 + pieCenterDegree;//
        }
    }

}
